{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 194,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import torch\n",
    "import torchvision.datasets as dset\n",
    "from torch.distributions import Bernoulli\n",
    "import torchvision.transforms as transforms\n",
    "import numpy as np\n",
    "import random\n",
    "from matplotlib import pyplot as plt\n",
    "from scipy.stats import halfnorm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deep Reinforcement Learning _in Action_\n",
    "## MNIST Genetic Algorithm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Setup a directory to store the MNIST dataset/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "root = './data'\n",
    "if not os.path.exists(root):\n",
    "    os.mkdir(root)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Setup a transformer to normalize the data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "trans = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (1.0,))])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz\n",
      "Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz\n",
      "Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz\n",
      "Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz\n",
      "Processing...\n",
      "Done!\n"
     ]
    }
   ],
   "source": [
    "train_set = dset.MNIST(root=root, train=True, transform=trans, download=True)\n",
    "test_set = dset.MNIST(root=root, train=False, transform=trans, download=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 100\n",
    "\n",
    "train_loader = torch.utils.data.DataLoader(\n",
    "                 dataset=train_set,\n",
    "                 batch_size=batch_size,\n",
    "                 shuffle=True)\n",
    "test_loader = torch.utils.data.DataLoader(\n",
    "                dataset=test_set,\n",
    "                batch_size=batch_size,\n",
    "                shuffle=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We define a simple linear classifier (or you can think of it as a single layer neural network). It simply multiplies a weight/parameter matrix by the input vector and applies a softmax."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = next(iter(train_loader))[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = x.reshape(100,784)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Individual:\n",
    "    def __init__(self,param, fitness=0):\n",
    "        self.param = param\n",
    "        self.fitness = fitness"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [],
   "source": [
    "def model(x,W):\n",
    "    return torch.nn.Softmax()(x @ W)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model(x,torch.rand(784,10))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 324,
   "metadata": {},
   "outputs": [],
   "source": [
    "def spawn_population(param_size=(784,10),pop_size=1000):\n",
    "    return [Individual(torch.randn(*param_size)) for i in range(pop_size)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_fn = torch.nn.CrossEntropyLoss()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 173,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 173,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "random.randint(0,10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 325,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate_population(pop):\n",
    "    avg_fit = 0 #avg population fitness\n",
    "    for individual in pop:\n",
    "        x,y = next(iter(train_loader))\n",
    "        pred = model(x.reshape(batch_size,784),individual.param)\n",
    "        loss = loss_fn(pred,y)\n",
    "        fit = loss\n",
    "        individual.fitness = 1.0 / fit\n",
    "        avg_fit += fit\n",
    "    avg_fit = avg_fit / len(pop)\n",
    "    return pop, avg_fit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 185,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([784, 10])"
      ]
     },
     "execution_count": 185,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pop[0].param.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 184,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([15680])"
      ]
     },
     "execution_count": 184,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.stack((pop[0].param.view(-1),pop[1].param.view(-1)),dim=0).view(-1).shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 269,
   "metadata": {},
   "outputs": [],
   "source": [
    "def recombine(x1,x2): #x1,x2 : Individual\n",
    "    w1 = x1.param.view(-1) #flatten\n",
    "    w2 = x2.param.view(-1)\n",
    "    cross_pt = random.randint(0,w1.shape[0])\n",
    "    child1 = torch.zeros(w1.shape)\n",
    "    child2 = torch.zeros(w1.shape)\n",
    "    child1[0:cross_pt] = w1[0:cross_pt]\n",
    "    child1[cross_pt:] = w2[cross_pt:]\n",
    "    child2[0:cross_pt] = w2[0:cross_pt]\n",
    "    child2[cross_pt:] = w1[cross_pt:]\n",
    "    child1 = child1.reshape(784,10)\n",
    "    child2 = child2.reshape(784,10)\n",
    "    c1 = Individual(child1)\n",
    "    c2 = Individual(child2)\n",
    "    return [c1,c2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 238,
   "metadata": {},
   "outputs": [],
   "source": [
    "def mutate(pop, mut_rate=0.01):\n",
    "    param_shape = pop[0].param.shape\n",
    "    l = torch.zeros(*param_shape)\n",
    "    l[:] = mut_rate\n",
    "    m = Bernoulli(l)\n",
    "    for individual in pop:\n",
    "        mut_vector = m.sample() * torch.randn(*param_shape)\n",
    "        individual.param = mut_vector + individual.param\n",
    "    return pop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 275,
   "metadata": {},
   "outputs": [],
   "source": [
    "def seed_next_population(pop,pop_size=1000, mut_rate=0.01):\n",
    "    new_pop = []\n",
    "    while len(new_pop) < pop_size: #until new pop is full\n",
    "        parents = random.choices(pop,k=2, weights=[x.fitness for x in pop])\n",
    "        offspring = recombine(parents[0],parents[1])\n",
    "        new_pop.extend(offspring)\n",
    "    new_pop = mutate(new_pop,mut_rate)\n",
    "    return new_pop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 304,
   "metadata": {},
   "outputs": [],
   "source": [
    "pop = spawn_population()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 305,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/brandonbrown/anaconda3/envs/deeprl/lib/python3.6/site-packages/ipykernel/__main__.py:2: UserWarning: Implicit dimension choice for softmax has been deprecated. Change the call to include dim=X as an argument.\n",
      "  from ipykernel import kernelapp as app\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 25.2 s, sys: 488 ms, total: 25.6 s\n",
      "Wall time: 13.2 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "pop, avg_fit = evaluate_population(pop)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 306,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_pop = seed_next_population(pop)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 307,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1000"
      ]
     },
     "execution_count": 307,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(new_pop)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we need to spawn a population of weight matrices, run the model using the different individuals, calculate the loss for each one, and then breed the ones with the highest fitness score (lowest loss)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 330,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_generations = 50\n",
    "population_size = 100\n",
    "mutation_rate = 0.001 # 1% mutation rate per generation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Main Evolution (Training) Loop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 331,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/brandonbrown/anaconda3/envs/deeprl/lib/python3.6/site-packages/ipykernel/__main__.py:2: UserWarning: Implicit dimension choice for softmax has been deprecated. Change the call to include dim=X as an argument.\n",
      "  from ipykernel import kernelapp as app\n"
     ]
    }
   ],
   "source": [
    "pop_fit = []\n",
    "pop = spawn_population(pop_size=population_size) #initial population\n",
    "for gen in range(num_generations):\n",
    "    # trainning\n",
    "    pop, avg_fit = evaluate_population(pop)\n",
    "    pop_fit.append(avg_fit) #record population average fitness\n",
    "    new_pop = seed_next_population(pop, pop_size=population_size, mut_rate=mutation_rate)\n",
    "    pop = new_pop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 332,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x11d2da240>]"
      ]
     },
     "execution_count": 332,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(pop_fit)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 328,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 329,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/brandonbrown/anaconda3/envs/deeprl/lib/python3.6/site-packages/ipykernel/__main__.py:2: UserWarning: Implicit dimension choice for softmax has been deprecated. Change the call to include dim=X as an argument.\n",
      "  from ipykernel import kernelapp as app\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(2.3526)\n"
     ]
    }
   ],
   "source": [
    "avg_loss = 0\n",
    "for i in range(len(pop)):\n",
    "    x,y = next(iter(train_loader))\n",
    "    pred = model(x.reshape(batch_size,784),pop[i].param)\n",
    "    loss = loss_fn(pred,y)\n",
    "    avg_loss += loss\n",
    "avg_loss /= len(pop)\n",
    "print(avg_loss)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Avg Loss new pop: 2.3336\n",
    "Avg Loss after 10 gens: 2.3435"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train with gradient-descent (comparison)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 347,
   "metadata": {},
   "outputs": [],
   "source": [
    "p = torch.randn(784,10, requires_grad=True)\n",
    "optim = torch.optim.Adam(lr=0.1, params=[p])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 348,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/brandonbrown/anaconda3/envs/deeprl/lib/python3.6/site-packages/ipykernel/__main__.py:2: UserWarning: Implicit dimension choice for softmax has been deprecated. Change the call to include dim=X as an argument.\n",
      "  from ipykernel import kernelapp as app\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(2.3012)\n",
      "tensor(2.2111)\n",
      "tensor(2.2311)\n",
      "tensor(2.2209)\n",
      "tensor(2.2511)\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-348-8c29280645b0>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mloss_list\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m50\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m     \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0my\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtrain_loader\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      4\u001b[0m         \u001b[0moptim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzero_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m         \u001b[0mpred\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m784\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deeprl/lib/python3.6/site-packages/torch/utils/data/dataloader.py\u001b[0m in \u001b[0;36m__next__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    262\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m  \u001b[0;31m# same-process loading\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    263\u001b[0m             \u001b[0mindices\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msample_iter\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# may raise StopIteration\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 264\u001b[0;31m             \u001b[0mbatch\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcollate_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mindices\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    265\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpin_memory\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    266\u001b[0m                 \u001b[0mbatch\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpin_memory_batch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deeprl/lib/python3.6/site-packages/torch/utils/data/dataloader.py\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m    262\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m  \u001b[0;31m# same-process loading\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    263\u001b[0m             \u001b[0mindices\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msample_iter\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# may raise StopIteration\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 264\u001b[0;31m             \u001b[0mbatch\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcollate_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mindices\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    265\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpin_memory\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    266\u001b[0m                 \u001b[0mbatch\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpin_memory_batch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deeprl/lib/python3.6/site-packages/torchvision-0.2.1-py3.6.egg/torchvision/datasets/mnist.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, index)\u001b[0m\n\u001b[1;32m     72\u001b[0m         \u001b[0;31m# doing this so that it is consistent with all other datasets\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     73\u001b[0m         \u001b[0;31m# to return a PIL Image\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 74\u001b[0;31m         \u001b[0mimg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mImage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfromarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnumpy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'L'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     75\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     76\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deeprl/lib/python3.6/site-packages/PIL/Image.py\u001b[0m in \u001b[0;36mfromarray\u001b[0;34m(obj, mode)\u001b[0m\n\u001b[1;32m   2448\u001b[0m             \u001b[0mobj\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mobj\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtostring\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2449\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2450\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mfrombuffer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msize\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"raw\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrawmode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   2451\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2452\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deeprl/lib/python3.6/site-packages/PIL/Image.py\u001b[0m in \u001b[0;36mfrombuffer\u001b[0;34m(mode, size, data, decoder_name, *args)\u001b[0m\n\u001b[1;32m   2394\u001b[0m             \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m  \u001b[0;31m# may change to (mode, 0, 1) post-1.1.6\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2395\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32min\u001b[0m \u001b[0m_MAPMODES\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2396\u001b[0;31m             \u001b[0mim\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnew\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   2397\u001b[0m             im = im._new(\n\u001b[1;32m   2398\u001b[0m                 \u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmap_buffer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msize\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdecoder_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deeprl/lib/python3.6/site-packages/PIL/Image.py\u001b[0m in \u001b[0;36mnew\u001b[0;34m(mode, size, color)\u001b[0m\n\u001b[1;32m   2297\u001b[0m         \u001b[0mcolor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mImageColor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetcolor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2298\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2299\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mImage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_new\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfill\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msize\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolor\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   2300\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2301\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "loss_list = []\n",
    "for i in range(50):\n",
    "    for x,y in train_loader:\n",
    "        optim.zero_grad()\n",
    "        pred = model(x.reshape(batch_size,784),p)\n",
    "        loss = loss_fn(pred,y)\n",
    "        loss_list.append(loss)\n",
    "        loss.backward()\n",
    "        optim.step()\n",
    "    print(loss)\n",
    "plt.plot(loss_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:deeprl]",
   "language": "python",
   "name": "conda-env-deeprl-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
